package pl.edu.pw.perg.compinfodb.datamodel;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

import org.springframework.richclient.security.ApplicationSecurityManager;
import org.springframework.richclient.security.support.DefaultApplicationSecurityManager;

import com.sun.org.apache.bcel.internal.generic.RETURN;

import pl.edu.pw.perg.compinfodb.command.IActiveModelListener;
import pl.edu.pw.perg.compinfodb.command.IModelModificationListener;
import pl.edu.pw.perg.compinfodb.command.IMutableTableModel;
import pl.edu.pw.perg.compinfodb.entities.HistoryItem;
import pl.edu.pw.perg.compinfodb.entities.PhysicalElement;
import pl.edu.pw.perg.compinfodb.entities.VirtualElement;
import pl.edu.pw.perg.compinfodb.manager.IHistoryManager;

public abstract class HistoryTableModel implements TableModel,
		IPhysicalElementSelectionListener, IMutableTableModel, FocusListener, IStructureTreeSelectionListener {

	private static final String DESC = "Description";
	private static final String AUTHOR = "Author";
	private static final String DATE = "Date";
	private static final String[] COLS_NAMES = { DESC, AUTHOR, DATE };
	
	protected IHistoryManager historyManager;
	protected List<TableModelListener> listeners = new ArrayList<TableModelListener>();
	private List<IEventModificationListener> evtModificationListeners = new ArrayList<IEventModificationListener>();
	private String[] columnNames;
	protected List<HistoryItem> history = new ArrayList<HistoryItem>();
	private List<HistoryItem> modified = new ArrayList<HistoryItem>();
	private List<IModelModificationListener> modelModificationListeners = new LinkedList<IModelModificationListener>();
	private List<IActiveModelListener> activeModelListeners = new LinkedList<IActiveModelListener>();
	private DefaultApplicationSecurityManager applicationSecurityManager;
	private String login;
	
	public HistoryTableModel(IHistoryManager historyManager) {
		this.historyManager = historyManager;
		columnNames = COLS_NAMES;
	}

	public void addTableModelListener(TableModelListener l) {
		listeners.add(l);
	}
	
	public void addModelModificationListener(IModelModificationListener l) {
		modelModificationListeners.add(l);

	}
	
	public void addActiveModelListener(IActiveModelListener l) {
		activeModelListeners.add(l);

	}

	public Class<?> getColumnClass(int columnIndex) {
		return String.class;
	}

	public int getColumnCount() {
		return columnNames.length;
	}

	public String getColumnName(int columnIndex) {
		return columnNames[columnIndex];
	}

	public int getRowCount() {
		return history.size();
	}

	public Object getValueAt(int rowIndex, int columnIndex) {
		HistoryItem event = history.get(rowIndex);

		if (getColumnName(columnIndex).equals(DATE))
			return event.getCreationDate();
		if (getColumnName(columnIndex).equals(DESC))
			return event.getDescription();
		if (getColumnName(columnIndex).equals(AUTHOR))
			return event.getAuthor();
		throw new IllegalArgumentException("No such column!");
	}

	public boolean isCellEditable(int rowIndex, int columnIndex) {
		if(history.get(rowIndex).getId() != null)
			return false;		
		if (getColumnName(columnIndex).equals(DATE))
			return false;
		if (getColumnName(columnIndex).equals(AUTHOR))
			return false;
//		TODO: trzeba by to lepiej rozwiazac, bo teraz trzeba uwazac przy dodawaniu kolumny
		return true;
	}

	public void removeTableModelListener(TableModelListener l) {
		listeners.remove(l);
	}

	public void setValueAt(Object value, int rowIndex, int columnIndex) {
		HistoryItem event = history.get(rowIndex);
		if (getColumnName(columnIndex).equals(DESC))
			event.setDescription((String) value);
		else if (getColumnName(columnIndex).equals(AUTHOR))
			event.setAuthor((String) value);
		else
			throw new IllegalArgumentException("No such column!");
		if (event.validate()) {
			modified.add(event);
		}
	}



	public void setHistoryManager(IHistoryManager historyManager) {
		this.historyManager = historyManager;
	}

	public abstract void selectionChanged(PhysicalElement pe);

	public void refresh(){
		history.clear();
		history.addAll(loadItems());
		for (TableModelListener tml : listeners) {
			tml.tableChanged(new TableModelEvent(this));
		}
		historyChanged(false);

	}
	
	protected abstract List<HistoryItem> loadItems();
	
	public void createEvent(){
		createEvent(null);
	}
	
	public void createEvent(String description){
		if(login == null)
			login = applicationSecurityManager.getAuthentication().getName();
		HistoryItem event = createItem();
		if(event == null)
			return;
		event.setAuthor(login);
		event.setCreationDate(new Date());
		if(description != null)
			event.setDescription(description);
		history.add(event);
		for (TableModelListener tml : listeners) {
			tml.tableChanged(new TableModelEvent(this));
		}
		if(event.validate())
			modified.add(event);
		historyChanged(true);
	}
	
	protected abstract HistoryItem createItem();
	
	public void saveItems(){
		
		List<HistoryItem> toSave = new ArrayList<HistoryItem>();
		for (HistoryItem event : modified) {
			if(event.getDescription() != null)
				toSave.add(event);
		}
		historyManager.saveAll(toSave);
		modified.clear();
		refresh();
		historyChanged(false);
	}

	public void addEventModifiactionListener(IEventModificationListener listener) {
		evtModificationListeners.add(listener);
	}
	
	public void historyChanged(boolean changed){
		for (IEventModificationListener l : evtModificationListeners) {
			l.historyChanged(changed);
		}
	}

	public boolean isModified() {
		return modified.size() > 0;
	}

	public void save() {
		saveItems();
		modelModified();
	}

	public void focusGained(FocusEvent e) {
		setActive();		
	}

	public void focusLost(FocusEvent e) {
//		do nothing		
	}

	public abstract void selectionChanged(VirtualElement ve);

	private void modelModified() {
		for (IModelModificationListener l : modelModificationListeners) {
			l.modelModified(isModified());
		}
	}
	
	public void reloadTable(){
		modified.clear();
		if(history.size() == 0)
			createEvent();
		for (TableModelListener table : listeners) {
			table.tableChanged(new TableModelEvent(this));
		}
	}

	public void setApplicationSecurityManager(
			ApplicationSecurityManager applicationSecurityManager) {
		this.applicationSecurityManager = (DefaultApplicationSecurityManager) applicationSecurityManager;
	}
	
	public void addNew() {
		createEvent();
		modelModified();
	}
	public void setActive() {
		for (IActiveModelListener l : activeModelListeners) {
			l.setActiveModel(this);
		}
		
	}
	
	public boolean isEditable() {
		return true;
	}
}
